const isVoid = (value) => value === undefined || value === null || value === "";
const arrayisEquality = (array, originArray) => {
  return array.some((item) => !originArray.includes(item));
};
const objectKeys = (object) => {
  if (object instanceof Object) {
    return Object.keys(object);
  } else {
    throw new Error("object must be Object");
  }
};
const supportRules = [
  "required",
  "maxLen",
  "minLen",
  "min",
  "max",
  "phone",
  "email",
];
const cleanObject = (object) => {
  const _result = {};
  for (let key in object) {
    if (!isVoid(object[key])) {
      _result[key] = object[key];
    }
  }
  return _result;
};

class CreateSchema {
  constructor(type, schema) {
    this.type = type;
    this.properties = {};
    this.required = [];
    this._formatToSchma(schema);
  }
  _formatToSchma(schema) {
    const _schemas = [...schema];

    const _required = _schemas
      .filter((schema) => schema?.validate?.match(/required/))
      .map((property) => property.key);
    this.required = _required;
    const _properties = {};
    _schemas.forEach((property) => {
      if (property?.validate) {
        const _formatFalidate = property.validate.replace(/\s/g, "");
        const _validate = _formatFalidate.split(",").map((item) => {
          if (/=/.test(item)) {
            return item.split("=")[0];
          } else {
            return item;
          }
        });

        _validate.forEach((validate) => {
          if (!supportRules.includes(validate)) {
            throw new Error(`only support ${supportRules.join(",")}`);
          }
        });

        _properties[property.key] = cleanObject({
          type: property.type,
          min: _formatFalidate?.match(/min=(\d+)/)
            ? Number(_formatFalidate.match(/min=(\d+)/)[1])
            : "",
          max: _formatFalidate?.match(/max=(\d+)/)
            ? Number(_formatFalidate.match(/max=(\d+)/)[1])
            : "",
          minLength: _formatFalidate?.match(/minLen=(\d+)/)
            ? Number(_formatFalidate.match(/minLen=(\d+)/)[1])
            : "",
          maxLength: _formatFalidate?.match(/maxLen=(\d+)/)
            ? Number(_formatFalidate.match(/maxLen=(\d+)/)[1])
            : "",
          email: _formatFalidate?.match(/email/)
            ? _formatFalidate?.match(/email/)[0]
            : "",
          phone: _formatFalidate?.match(/phone/)
            ? _formatFalidate?.match(/phone/)[0]
            : "",
        });
      }
    });

    this.properties = _properties;
  }
}

class CustomCompile {
  static errors=[]
  static TYPES = {
    object: Object,
    array: Array,
    string: String,
    number: Number,
  };
  static STRINGVALIDATE = ["required", "maxLength", "minLength", "email"];
  static NUMBERVALIDATE = ["required", "max", "min", "phone"];
  constructor() {
 }
  static judgementMaxLength(data, maxLength) {
    if (data.length <= maxLength) {
      return true;
    } else {
      return false;
    }
  }
  static judgementMinLength(data, minLength) {
    if (data.length >= minLength) {
      return true;
    } else {
      return false;
    }
  }
  static judgementEmail(data) {
    if (
      /^[a-z0-9A-Z]+[-|a-z0-9A-Z._]+@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?.)+[a-z]{2,}$/.test(
        data
      )
    ) {
      return true;
    } else {
      return false;
    }
  }
  static judgementMax(data, max) {
    if (data <= max) {
      return true;
    } else {
      return false;
    }
  }
  static judgementMin(data, min) {
    if (data >= min) {
      return true;
    } else {
      return false;
    }
  }
  static judgementPhone(data) {
    if (/^1[3456789]\d{9}$/.test(data)) {
      return true;
    } else {
      return false;
    }
  }
  static judgementString(_rules, _ruleType, _rulesObjectkeys, data, require) {
    if (!arrayisEquality(_rulesObjectkeys, CustomCompile.STRINGVALIDATE)) {
      if (_rules["email"]) {
        if (_rules["maxLength"] || _rules["minLength"]) {
          console.log(this)
          throw new Error(`email not needs maxLength or minLength `);
        }
        if (!CustomCompile.judgementEmail(data[require])) {
          this.errors.push({
            z:0
          })
          throw new Error(`${require} not in accordance with the rules `);
        }
      } else {
        if (_rules["maxLength"]) {
          if (
            !CustomCompile.judgementMaxLength(data[require], _rules.maxLength)
          ) {
            throw new Error(`${require} is must be <=maxLen`);
          }
        }
        if (_rules["minLength"]) {
          if (
            !CustomCompile.judgementMinLength(data[require], _rules.minLength)
          ) {
            throw new Error(`${require} is must be >=minLen`);
          }
        }
      }
    } else {
      throw new Error(
        `string validate  only support required", "minLen", "maxLen", "email"`
      );
    }
  }
  static judgementNumber(_rules, _ruleType, _rulesObjectkeys, data, require) {
    if (!arrayisEquality(_rulesObjectkeys, CustomCompile.NUMBERVALIDATE)) {
      if (_rules["phone"]) {
        if (_rules["max"] || _rules["min"]) {
          throw new Error(`phone not needs max or min `);
        }
        if (!CustomCompile.judgementPhone(data[require])) {
          throw new Error(`${require} is  not in accordance with the rules`);
        }
      } else {
        if (_rules["max"]) {
          if (!CustomCompile.judgementMax(data[require], _rules.max)) {
            throw new Error(`${require} is must be <=max`);
          }
        }
        if (_rules["min"]) {
          if (!CustomCompile.judgementMin(data[require], _rules.min)) {
            throw new Error(`${require} is must be >=min`);
          }
        }
      }
    } else {
      throw new Error(
        `number support vaildate ${CustomCompile.NUMBERVALIDATE.join(",")}`
      );
    }
  }
  static judgementRequired(required, properties) {
    if (required.length > 0) {
      required.forEach((require) => {
        if (!data[require]) {
          throw new Error(`${require} are required in data`);
        } else {
          const _ruleType = properties[require]?.type;
          if (_ruleType) {
            if (typeof data[require] === _ruleType) {
              const _rules = cleanObject({
                ...properties[require],
                type: "",
              });
              const _rulesObjectkeys = objectKeys(_rules);
              if (_ruleType === "string") {
                CustomCompile.judgementString(
                  _rules,
                  _ruleType,
                  _rulesObjectkeys,
                  data,
                  require
                );
              }
              if (_ruleType === "number") {
                CustomCompile.judgementNumber(
                  _rules,
                  _ruleType,
                  _rulesObjectkeys,
                  data,
                  require
                );
              }
            } else {
              throw new Error(
                `The ${require} and rule in data do not match propertyKey must be ${_ruleType}`
              );
            }
          } else {
            throw new Error(
              `The ${require} type field is required with "requreid"`
            );
          }
        }
      });
    } else {
      for (let key in data) {
        if (Object.keys(properties[key].length > 0)) {
          if (typeof data[key] === "string") {
          } else {
            throw new Error(
              `${key} not in accordance with the rules it is should be string`
            );
          }
        }
      }
    }
  }

  compile(data, rules) {
    const type = CustomCompile.TYPES[rules.type];
    const properties = rules.properties;
    const required = rules.required;
    if (type) {
      if (rules.type === "object" && data?.constructor === type) {
        CustomCompile.judgementRequired(required, properties);
      } else {
       // throw new Error("data muse be object");
        const error={
          message:"The source object type must be an object"
        }
       return  CustomCompile.errors=[error]
      }
    } else {
      throw new Error(
        "CustomCompile only support object array string or number"
      );
    }
  }
}


const data = {
  username: "asda",
  password: "11955552592@qq.com",
  age: 6,
  phone: 13042925511,
};

const schema = new CreateSchema("object", [
  { key: "password", type: "string", validate: "required,email" },
  { key: "username", type: "string", validate: "required,maxLen=6" },
  { key: "age", type: "number", validate: "required,max=24,min =5" },
  { key: "phone", type: "number", validate: "required,phone" },
]);


const customCompile = new CustomCompile();
console.log(customCompile.compile(data, schema))
